package com.icesoft.xsnow.common.log.aspect;

import com.icesoft.xsnow.common.core.constant.enums.HTTPMethod;
import com.icesoft.xsnow.common.log.annotation.SysLog;
import com.icesoft.xsnow.common.log.event.SysLogEvent;
import com.icesoft.xsnow.common.log.util.SysLogUtils;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @program: xsnow
 * @description: 操作日志切面业务
 * 1.捕获日志
 * 2.使用spring event异步入库
 * @author: xuefeng.gao
 * @create: 2019-06-06 09:44
 **/
@Slf4j
@Aspect
@AllArgsConstructor
public class SysLogAspect {
    private final ApplicationEventPublisher publisher;

    /**
    * Service切面入口: 基于@SysLog
    **/
    @Around("@annotation(sysLog)")
    @Order(100)
    public Object aspectBySysLog(ProceedingJoinPoint point, SysLog sysLog) {
        return saveSysLog(point,sysLog.value());
    }

    /**
    * Controller切面入口:
     * 1.基于@ApiOperation注解
     * 2.Get请求全部忽略
    **/
    @Around("@annotation(apiOperation)")
    @Order(90)
    @SneakyThrows
    public Object aspectByApiOperation(ProceedingJoinPoint point, ApiOperation apiOperation) {
        //Get请求放行
        HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if(HTTPMethod.GET.name().equalsIgnoreCase(req.getMethod())){
            return point.proceed();
        }
        //其他的,记录日志
        return saveSysLog(point,apiOperation.value());
    }

    /**
    * 创建系统日志
     **/
    @SneakyThrows
    private Object saveSysLog(ProceedingJoinPoint point, String title) {
        String strClassName = point.getTarget().getClass().getName();
        String strMethodName = point.getSignature().getName();
        log.debug("[类名]:{},[方法]:{}",strClassName,strMethodName);

        com.icesoft.xsnow.framework.api.po.SysLog logVO = SysLogUtils.getSysLog();
        logVO.setTitle(title);
        // 发送异步日志事件
        Long startTime = System.currentTimeMillis();

        // <3>在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法
        // 如果忘记这样做就会导致通知被执行了,  但目标方法没有被执行
        Object obj = point.proceed();

        Long endTime = System.currentTimeMillis();
        logVO.setTime(endTime - startTime);
        publisher.publishEvent(new SysLogEvent(logVO));

        //<4>.环绕通知的方法需要返回目标方法执行之后的结果, 即调用 joinPoint.proceed(); 的返回值, 否则会出现空指针异常
        return obj;
    }

}
